package cn.juque.systemservice.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.juque.common.base.PageInfo;
import cn.juque.common.constants.MessageEnum;
import cn.juque.common.exception.AppException;
import cn.juque.jdfsystem.api.FileInfoClient;
import cn.juque.systemservice.api.constant.EmployeeStatusEnum;
import cn.juque.systemservice.api.constant.SystemServiceMsgEnum;
import cn.juque.systemservice.bo.employee.*;
import cn.juque.systemservice.cache.EmployeeCache;
import cn.juque.systemservice.cache.OrgCache;
import cn.juque.systemservice.domain.EmployeeOrgRelationDO;
import cn.juque.systemservice.domain.EmployeeRoleRelationDO;
import cn.juque.systemservice.domain.OrgInfoDO;
import cn.juque.systemservice.domain.RoleFuncRelationDO;
import cn.juque.systemservice.entity.EmployeeInfo;
import cn.juque.systemservice.entity.EmployeeOrgRelation;
import cn.juque.systemservice.entity.EmployeeRoleRelation;
import cn.juque.systemservice.entity.RoleFuncRelation;
import cn.juque.systemservice.mapper.EmployeeInfoMapper;
import cn.juque.systemservice.mapper.EmployeeOrgRelationMapper;
import cn.juque.systemservice.mapper.EmployeeRoleRelationMapper;
import cn.juque.systemservice.mapper.RoleFuncRelationMapper;
import cn.juque.systemservice.service.IEmployeeInfoService;
import cn.juque.systemservice.service.IUserInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author JUQUE
 * @version 1.0.0
 * <ul>
 *  <li>simple-generator</li>
 *  <li>员工信息</li>
 * </ul>
 * @date 2022-11-03 11:58:48
 **/
@Service("employeeInfoService")
public class EmployeeInfoServiceImpl implements IEmployeeInfoService {

    @Resource
    private IUserInfoService userInfoService;

    @Resource
    private EmployeeInfoMapper employeeInfoMapper;

    @Resource
    private EmployeeOrgRelationMapper employeeOrgRelationMapper;

    @Resource
    private EmployeeRoleRelationMapper employeeRoleRelationMapper;

    @Resource
    private RoleFuncRelationMapper roleFuncRelationMapper;

    @Resource
    private EmployeeCache employeeCache;

    @Resource
    private OrgCache orgCache;

    @Resource
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Resource
    private FileInfoClient consumerFileClient;

    /**
     * 分页查询
     *
     * @param pageInfo     pageInfo
     * @param employeeInfo employeeInfo
     * @return DataGrid
     */
    @Override
    public List<EmployeeInfo> listInfoWithPage(PageInfo pageInfo, EmployeeInfo employeeInfo) {
        Page<EmployeeInfo> page = new Page<>(pageInfo.getPage(), pageInfo.getLimit());
        List<EmployeeInfo> list = this.employeeInfoMapper.queryInfoList(page, employeeInfo);
        pageInfo.setTotal(page.getTotal());
        return list;
    }

    /**
     * 查询
     *
     * @param employeeInfo employeeInfo
     * @return List
     */
    @Override
    public List<EmployeeInfo> listInfoWithoutPage(EmployeeInfo employeeInfo) {
        return this.employeeInfoMapper.queryInfoList(employeeInfo);
    }

    /**
     * 详情
     *
     * @param id 主键
     * @return EmployeeInfo
     */
    @Override
    public EmployeeInfoDetailBO detail(String id) {
        EmployeeInfo employeeInfo = this.employeeInfoMapper.selectById(id);
        EmployeeInfoDetailBO bo = new EmployeeInfoDetailBO();
        BeanUtil.copyProperties(employeeInfo, bo);
        this.buildEmployeeInfoDetailBO(CollUtil.newArrayList(bo));
        return bo;
    }

    /**
     * 获取密码
     *
     * @param employeePasswordBO EmployeePasswordBO
     * @return String
     */
    @Override
    public EmployeeInfo getPassword(EmployeePasswordBO employeePasswordBO) {
        EmployeeInfo employeeInfo = null;
        if (CharSequenceUtil.isNotEmpty(employeePasswordBO.getEmployeeTel())) {
            employeeInfo = this.employeeInfoMapper.queryInfoByTel(employeePasswordBO.getEmployeeTel());
        }
        if (null == employeeInfo && CharSequenceUtil.isNotEmpty(employeePasswordBO.getEmail())) {
            employeeInfo = this.employeeInfoMapper.queryInfoByMail(employeePasswordBO.getEmail());
        }
        if (null == employeeInfo && null != employeePasswordBO.getEmployeeId()) {
            employeeInfo = this.employeeInfoMapper.selectById(employeePasswordBO.getEmployeeId());
        }
        if (null != employeeInfo && !EmployeeStatusEnum.ENABLE.getCode().equals(employeeInfo.getEmployeeStatus())) {
            throw new AppException(SystemServiceMsgEnum.EMPLOYEE_DISABLED);
        }
        return employeeInfo;
    }

    /**
     * 根据员工号获取员工信息
     *
     * @param userIdList 员工号
     * @return list
     */
    @Override
    public List<EmployeeInfoDetailBO> getByUserId(List<String> userIdList) {
        if (CollUtil.isEmpty(userIdList)) {
            return CollUtil.newArrayList();
        }
        List<EmployeeInfo> list = this.employeeInfoMapper.queryInfoByUserId(userIdList);
        List<EmployeeInfoDetailBO> boList = BeanUtil.copyToList(list, EmployeeInfoDetailBO.class);
        if (CollUtil.isEmpty(boList)) {
            return CollUtil.newArrayList();
        }
        this.buildEmployeeInfoDetailBO(boList);
        return boList;
    }

    /**
     * 根据角色获取员工信息
     *
     * @param roleIdList 员工号
     * @return list
     */
    @Override
    public List<EmployeeInfoDetailBO> getByRoleId(List<String> roleIdList) {
        if (CollUtil.isEmpty(roleIdList)) {
            return CollUtil.newArrayList();
        }
        EmployeeRoleRelationDO query = new EmployeeRoleRelationDO();
        query.setRoleIdList(roleIdList);
        List<EmployeeRoleRelationDO> list = this.employeeRoleRelationMapper.queryRoleRelationList(query);
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        List<String> employeeIdList = list.stream().map(EmployeeRoleRelation::getEmployeeId).collect(Collectors.toList());
        List<EmployeeInfo> employeeList = this.employeeInfoMapper.selectBatchIds(employeeIdList);
        List<EmployeeInfoDetailBO> detailList = BeanUtil.copyToList(employeeList, EmployeeInfoDetailBO.class);
        this.buildEmployeeInfoDetailBO(detailList);
        return detailList;
    }

    /**
     * 保存
     *
     * @param employeeInfoAddBO employeeInfoAddBO
     */
    @Override
    public void saveInfo(EmployeeInfoAddBO employeeInfoAddBO) {
        // 手机号、邮箱号唯一性
        EmployeeInfo employeeInfo = this.employeeInfoMapper.queryInfoByTel(employeeInfoAddBO.getEmployeeTel());
        if (null == employeeInfo) {
            employeeInfo = this.employeeInfoMapper.queryInfoByMail(employeeInfoAddBO.getEmail());
        }
        if (null != employeeInfo) {
            throw new AppException(SystemServiceMsgEnum.TEL_OR_MAIL_EXIST);
        }
        // 创建用户
        String userId = this.userInfoService.saveInfo();
        EmployeeInfo entity = new EmployeeInfo();
        BeanUtil.copyProperties(employeeInfoAddBO, entity);
        entity.setUserId(userId);
        entity.setEmployeePassword(this.bCryptPasswordEncoder.encode(entity.getEmployeePassword()));
        boolean flag = SqlHelper.retBool(this.employeeInfoMapper.insert(entity));
        if (!flag) {
            throw new AppException(MessageEnum.SYSTEM_FAIL.getCode(), "保存失败");
        }
        this.saveOrgRelation(entity, employeeInfoAddBO.getOrgIdList());
        this.saveRoleRelation(entity, employeeInfoAddBO.getRoleIdList());
        this.employeeCache.put(entity);
        this.consumerFileClient.trustFile(CollUtil.newArrayList(entity.getHeadUrl()), CollUtil.newArrayList());
    }

    /**
     * 更新
     *
     * @param employeeInfoUpdateBO employeeInfoUpdateBO
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateInfo(EmployeeInfoUpdateBO employeeInfoUpdateBO) {
        EmployeeInfo source = this.employeeInfoMapper.selectById(employeeInfoUpdateBO.getId());
        EmployeeInfo entity = new EmployeeInfo();
        BeanUtil.copyProperties(employeeInfoUpdateBO, entity);
        boolean flag = SqlHelper.retBool(this.employeeInfoMapper.updateById(entity));
        if (!flag) {
            throw new AppException(MessageEnum.SYSTEM_FAIL.getCode(), "更新失败");
        }
        this.saveOrgRelation(entity, employeeInfoUpdateBO.getOrgIdList());
        this.saveRoleRelation(entity, employeeInfoUpdateBO.getRoleIdList());
        this.employeeCache.delete(CollUtil.newArrayList(entity.getId()));
        if (CharSequenceUtil.isNotEmpty(source.getHeadUrl())
                && !source.getHeadUrl().equals(entity.getHeadUrl())) {
            this.consumerFileClient.trustFile(CollUtil.newArrayList(entity.getHeadUrl()), CollUtil.newArrayList(source.getHeadUrl()));
        }
    }

    /**
     * 更新密码
     *
     * @param employeeId  员工ID
     * @param password    原密码
     * @param newPass     新密码
     * @param confirmPass 确认密码
     */
    @Override
    public void updatePassword(String employeeId, String password, String newPass, String confirmPass) {
        EmployeeInfo employeeInfo = this.employeeInfoMapper.selectById(employeeId);
        if (!this.bCryptPasswordEncoder.matches(password, employeeInfo.getEmployeePassword())) {
            throw new AppException(SystemServiceMsgEnum.PASS_ERROR);
        }
        if (!newPass.equals(confirmPass)) {
            throw new AppException(SystemServiceMsgEnum.PASS_CONFIRM_ERROR);
        }
        employeeInfo.setEmployeePassword(this.bCryptPasswordEncoder.encode(newPass));
        this.employeeInfoMapper.updateById(employeeInfo);
    }

    /**
     * 删除
     *
     * @param ids 主键列表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteInfo(List<String> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        List<EmployeeInfo> list = this.employeeInfoMapper.selectBatchIds(ids);
        int count = this.employeeInfoMapper.deleteBatchIds(ids);
        if (count != ids.size()) {
            throw new AppException(MessageEnum.DELETE_FAIL);
        }
        List<String> fileIdList = list.stream().map(EmployeeInfo::getHeadUrl).collect(Collectors.toList());
        this.consumerFileClient.trustFile(CollUtil.newArrayList(), fileIdList);
    }

    private void saveOrgRelation(EmployeeInfo employeeInfo, List<String> orgIdList) {
        // 先删除
        EmployeeOrgRelation delQuery = new EmployeeOrgRelation();
        delQuery.setEmployeeId(employeeInfo.getId());
        this.employeeOrgRelationMapper.delete(new QueryWrapper<>(delQuery));
        if (CollUtil.isEmpty(orgIdList)) {
            return;
        }
        // 去重，保存
        orgIdList = orgIdList.stream().distinct().collect(Collectors.toList());
        orgIdList.forEach(t -> {
            EmployeeOrgRelation relation = new EmployeeOrgRelation();
            relation.setEmployeeId(employeeInfo.getId());
            relation.setOrgId(t);
            this.employeeOrgRelationMapper.insert(relation);
        });
    }

    private void saveRoleRelation(EmployeeInfo employeeInfo, List<String> roleIdList) {
        // 删除
        EmployeeRoleRelation delQuery = new EmployeeRoleRelation();
        delQuery.setEmployeeId(employeeInfo.getId());
        this.employeeRoleRelationMapper.delete(new QueryWrapper<>(delQuery));
        if (CollUtil.isEmpty(roleIdList)) {
            return;
        }
        // 去重，保存
        roleIdList = roleIdList.stream().distinct().collect(Collectors.toList());
        roleIdList.forEach(t -> {
            EmployeeRoleRelation relation = new EmployeeRoleRelation();
            relation.setRoleId(t);
            relation.setEmployeeId(employeeInfo.getId());
            this.employeeRoleRelationMapper.insert(relation);
        });
    }

    private void buildEmployeeInfoDetailBO(List<EmployeeInfoDetailBO> boList) {
        List<String> employeeIdList = boList.stream().map(EmployeeInfoDetailBO::getId).collect(Collectors.toList());
        EmployeeOrgRelationDO orgRelationDO = new EmployeeOrgRelationDO();
        orgRelationDO.setEmployeeIdList(employeeIdList);
        List<EmployeeOrgRelationDO> orgRelationResult = this.employeeOrgRelationMapper.queryOrgRelationList(orgRelationDO);
        List<EmployeeOrgRelationBO> orgRelationBoList = BeanUtil.copyToList(orgRelationResult, EmployeeOrgRelationBO.class);
        // 赋值机构信息
        orgRelationBoList.parallelStream().forEach(t -> {
            OrgInfoDO orgInfoDO = this.orgCache.get(t.getOrgId());
            BeanUtil.copyProperties(orgInfoDO, t);
        });
        Map<String, List<EmployeeOrgRelationBO>> orgRelationMap = orgRelationBoList.stream().collect(Collectors.groupingBy(
                EmployeeOrgRelationBO::getEmployeeId));

        EmployeeRoleRelationDO roleRelationDO = new EmployeeRoleRelationDO();
        roleRelationDO.setEmployeeIdList(employeeIdList);
        List<EmployeeRoleRelationDO> roleRelationResult = this.employeeRoleRelationMapper.queryRoleRelationList(roleRelationDO);
        List<EmployeeRoleRelationBO> roleRelationBoList = BeanUtil.copyToList(roleRelationResult, EmployeeRoleRelationBO.class);
        Map<String, List<EmployeeRoleRelationBO>> roleRelationMap = roleRelationBoList.stream().collect(Collectors.groupingBy(
                EmployeeRoleRelationBO::getEmployeeId));
        // 获取员工与机构、角色的关系
        boList.forEach(t -> {
            t.setOrgList(orgRelationMap.getOrDefault(t.getId(), CollUtil.newArrayList()));
            t.setRoleList(roleRelationMap.getOrDefault(t.getId(), CollUtil.newArrayList()));
        });
        List<String> roleIdList = roleRelationResult.stream().map(EmployeeRoleRelation::getRoleId).collect(Collectors.toList());
        RoleFuncRelationDO roleFuncRelationDO = new RoleFuncRelationDO();
        roleFuncRelationDO.setRoleIdList(roleIdList);
        List<RoleFuncRelationDO> funcRelationList = this.roleFuncRelationMapper.queryFuncRelationList(roleFuncRelationDO);
        // 角色与功能的关系
        Map<String, List<String>> roleRelFuncMap = funcRelationList.stream()
                .collect(Collectors.groupingBy(RoleFuncRelation::getRoleId,
                        Collectors.mapping(RoleFuncRelation::getMenuId, Collectors.toList())));
        // 功能关系取并集
        boList.forEach(b -> {
            Set<String> roleIdSet = b.getRoleList().stream().map(EmployeeRoleRelationBO::getRoleId).collect(Collectors.toSet());
            Set<String> funcIdSet = CollUtil.newHashSet();
            roleRelFuncMap.forEach((k, v) -> {
                if (roleIdSet.contains(k)) {
                    funcIdSet.addAll(v);
                }
            });
            b.setFuncIdList(CollUtil.newArrayList(funcIdSet));
        });
    }

}
